package com.portal.tag;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.portlet.BaseURL;
import javax.portlet.PortletSecurityException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagData;
import javax.servlet.jsp.tagext.TagExtraInfo;
import javax.servlet.jsp.tagext.TagSupport;
import javax.servlet.jsp.tagext.VariableInfo;

import com.portal.util.StringUtil;

public abstract class BaseURLTag extends TagSupport {

  protected String secure = null;

  protected Boolean secureBoolean = null;

  protected String var = null;

  protected Boolean escapeXml = true;

  protected Map<String, List<String>> parametersMap = new HashMap<String, List<String>>();

  protected List<String> removedParametersList = new ArrayList<String>();

  protected Map<String, List<String>> propertiesMap = new HashMap<String, List<String>>();

  @Override
  public int doStartTag() throws JspException {
    BaseURL url = getUrl();
    if (url == null) {
      throw new IllegalStateException("internal error: url not set");
    }
    if (var != null) {
      pageContext.removeAttribute(var, PageContext.PAGE_SCOPE);
    }
    if (secure != null) {
      try {
        url.setSecure(getSecureBoolean());
      } catch (PortletSecurityException e) {
        throw new JspException(e);
      }
    }
    return EVAL_BODY_INCLUDE;
  }

  @Override
  public int doEndTag() throws JspException {
    BaseURL url = getUrl();
    if (url == null) {
      throw new IllegalStateException("internal error: url not set");
    }

    setUrlParameters(url);
    setUrlProperties(url);

    HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
    String urlString = response.encodeURL(url.toString());

    if (escapeXml) {
      urlString = doEscapeXml(urlString);
    }

    if (var == null) {
      try {
        JspWriter writer = pageContext.getOut();
        writer.print(urlString);
      } catch (IOException ioe) {
        throw new JspException("Portlet/ResourceURL-Tag Exception: cannot write to the output writer.");
      }
    } else {
      pageContext.setAttribute(var, urlString, PageContext.PAGE_SCOPE);
    }

    propertiesMap.clear();
    parametersMap.clear();
    removedParametersList.clear();
    setUrl(null);

    return EVAL_PAGE;
  }

  @Override
  public void release() {
    secureBoolean = null;
    super.release();
  }

  public String getSecure() {
    return secure;
  }

  public boolean getSecureBoolean() {
    if (this.secureBoolean != null) {
      return this.secureBoolean.booleanValue();
    } else {
      return false;
    }
  }

  public String getVar() {
    return var;
  }

  public Boolean getEscapeXml() {
    return escapeXml;
  }

  public void setSecure(String secure) {
    this.secure = secure;
    this.secureBoolean = new Boolean(secure);
  }

  public void setVar(String var) {
    this.var = var;
  }

  public void setEscapeXml(Boolean escapeXml) {
    this.escapeXml = escapeXml;
  }

  protected void addParameter(String key, String value) {
    if (StringUtil.isNullOrEmpty(key)) {
      throw new IllegalArgumentException("the argument key must not be null or empty!");
    }

    if (StringUtil.isNullOrEmpty(value)) { // remove value
      if (parametersMap.containsKey(key)) {
        parametersMap.remove(key);
      }
      removedParametersList.add(key);
    } else { // add value
      List<String> valueList = parametersMap.containsKey(key) ? parametersMap.get(key) : new ArrayList<String>();
      valueList.add(value);
      parametersMap.put(key, valueList);
    }
  }

  protected void addProperty(String key, String value) {
    if (key == null) {
      throw new NullPointerException();
    }

    List<String> valueList = propertiesMap.containsKey(key) ? propertiesMap.get(key) : new ArrayList<String>();
    valueList.add(value);
    propertiesMap.put(key, valueList);

  }

  protected void setUrlParameters(BaseURL url) {
    Set<String> keySet = parametersMap.keySet();

    for (String key : keySet) {
      List<String> valueList = parametersMap.get(key);
      String[] valueArray = valueList.toArray(new String[0]);
      url.setParameter(key, valueArray);
    }
  }

  protected void setUrlProperties(BaseURL url) {
    Set<String> keySet = propertiesMap.keySet();

    for (String key : keySet) {
      List<String> valueList = propertiesMap.get(key);
      for (String value : valueList) {
        url.addProperty(key, value);
      }
    }
  }

  protected abstract BaseURL getUrl();

  protected abstract void setUrl(BaseURL url);

  protected String doEscapeXml(String str) {
    if (StringUtil.isNotNullNorEmpty(str)) {
      str = str.replaceAll("&", "&amp;");
      str = str.replaceAll("<", "&lt;");
      str = str.replaceAll(">", "&gt;");
      str = str.replaceAll("\"", "&#034;");
      str = str.replaceAll("'", "&#039;");
    }
    return str;
  }

  public static class TEI extends TagExtraInfo {

    public VariableInfo[] getVariableInfo(TagData tagData) {
      VariableInfo vi[] = null;
      String var = tagData.getAttributeString("var");
      if (var != null) {
        vi = new VariableInfo[1];
        vi[0] = new VariableInfo(var, "java.lang.String", true, VariableInfo.AT_BEGIN);
      }
      return vi;
    }

  }
}
